//The MIT License (MIT)
//
//Copyright (c) 2018 dote
//
//Permission is hereby granted, free of charge, to any person obtaining a copy of
//this software and associated documentation files (the "Software"), to deal in
//the Software without restriction, including without limitation the rights to
//use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
//the Software, and to permit persons to whom the Software is furnished to do so,
//subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
//FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
//COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
//IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
//CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package com.github.dote.mio.server.handler;

import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.listener.ConnectListener;
import com.corundumstudio.socketio.listener.DataListener;
import com.corundumstudio.socketio.listener.DisconnectListener;
import com.github.dote.mio.client.UdpClient;
import com.github.dote.mio.client.IOClient;
import com.github.dote.mio.client.WebSocketClient;
import com.github.dote.mio.protocal.Packet;
import com.github.dote.mio.protocal.PacketType;
import com.github.dote.mio.rtt.RTTHandler;
import com.github.dote.mio.server.IOServer;
import com.github.dote.mio.util.ByteBufUtil;
import com.github.dote.mio.util.ChannelUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;

/**
 * PacketHandler
 */
@Slf4j
public class PacketHandler {

    private IOServer server = null;

    public PacketHandler(IOServer server) {
        this.server = server;


    }

    public void socketIOEventInit() {
        if (server.getSocketIOServer() != null) {
            server.getSocketIOServer().addConnectListener(new ConnectListener() {
                @Override
                public void onConnect(SocketIOClient socketIOClient) {
                    IOClient client = new WebSocketClient(socketIOClient);
                    server.getSessionManager().addClient(client.getSessionId(), client);
                    server.getEventManager().onConnect(client);
                }
            });

            server.getSocketIOServer().addDisconnectListener(new DisconnectListener() {
                @Override
                public void onDisconnect(SocketIOClient socketIOClient) {
                    IOClient client = server.getSessionManager().getClient(socketIOClient.getSessionId().toString());
                    if (client != null) {
                        server.getSessionManager().removeClient(client.getSessionId());
                        server.getEventManager().onDisconnect(client);
                    }
                }
            });

            server.getSocketIOServer().addEventListener(WebSocketClient.PACKET_EVENT_NAME, Packet.class, new DataListener<Packet>() {
                @Override
                public void onData(SocketIOClient socketIOClient, Packet packet, AckRequest ackRequest) throws Exception {
                    IOClient client = server.getSessionManager().getClient(socketIOClient.getSessionId().toString());
                    if (packet != null && client != null) {
                        handler(client, packet);
                    }
                }
            });
        }
    }

    //udp 一些参数
    public void connect(ChannelHandlerContext ctx) {
        Channel channel = ctx.channel();
        IOClient client = new UdpClient(server.getAckSendTask(), channel);
        client.setRTTHandler(new RTTHandler(server.getConfig().getAckTimeout()));
        if (ChannelUtil.addChannelClient(channel, client)) {
            server.getEventManager().onConnect(client);
            server.getSessionManager().addClient(client.getSessionId(), client);
        }
    }

    public void disconnect(ChannelHandlerContext ctx) {
        Channel channel = ctx.channel();
        IOClient client = ChannelUtil.getClientBy(channel);
        disconnect(client);
    }

    public void disconnect(IOClient client) {
        if (client.close()) {
            server.getEventManager().onDisconnect(client);
            server.getSessionManager().removeClient(client.getSessionId());
        }
    }

    public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) {
        log.error(e.getMessage(), e);
        disconnect(ctx);
    }


    public void handler(Channel channel, ByteBuf byteBuf) throws Exception {
        IOClient client = ChannelUtil.getClientBy(channel);
        Packet packet = ByteBufUtil.toPacket(byteBuf);

        if (packet != null && client != null) {

            handler(client, packet);

            byteBuf.clear();
        }
    }

    private void handler(IOClient client, Packet packet) {
        switch (packet.getType()) {
            case PacketType.KEEP_ALIVE: {
                server.getKeepAliveProcessor().processKeepAlive(client);
                break;
            }
            case PacketType.ACK: {
                server.getAckProcessor().processAck(client, packet);
                break;
            }
            case PacketType.EVENT_MESSAGE: {
                server.getEventMessageProcessor().processEventMessage(client, packet);
                break;
            }
            case PacketType.ECHO: {
                packet.setSessionId(client.getSessionId());
                client.send(packet);
                break;
            }
            default:
                break;
        }
    }

}
